#pragma once
#include "yzrutil.h"
#include "lang/String.h"
#include "lang/Exception.h"
#include "lang/ObjectUtil.hpp"
#include "lang/RefObject.h"
#include <initializer_list>

namespace yzrilyzr_array{
	template<typename T>
	ECLASS(Array, public yzrilyzr_lang::Object, public yzrilyzr_lang::RefObject<T>){
	protected:
	explicit Array(T * arr, u_index length, bool canDelete) :_array(arr), length(length){
		if(canDelete)this->refDoConstruct(_array);
	}
	void refDeleteInternal()override{
		delete[] _array;
		_array=nullptr;
	}
	T * refCopyOnWriteFrom()override{
		throw yzrilyzr_lang::UnsupportedOperationException();
	}
	void refCopyFrom(const yzrilyzr_lang::RefObject<T> & other)override{
		const Array & copy=static_cast<const Array &>(other);
		_array=copy._array;
		length=copy.length;
	}

	public:
	T * _array=nullptr;
	u_index length=0;

	constexpr Array() :length(-1), _array(nullptr){}

	Array(const Array & copy){
		this->refDoCopy(copy);
	}
	Array & operator=(const Array & copy){
		this->refDoAssignCopy(copy);
		return *this;
	}

	explicit constexpr Array(std::initializer_list<T> init) : length(init.size()){
		if(length > 1024 * 1024 * 1024){
			throw yzrilyzr_lang::IndexOutOfBoundsException("Check size failed");
		}
		_array=new T[length];
		if(std::is_trivially_copyable<T>::value){
			std::copy(init.begin(), init.end(), _array);
		} else{
			auto it=init.begin();
			for(size_t i=0; i < length; i++, it++){
				_array[i]=*it;
			}
		}
		this->refDoConstruct(_array);
	}

	explicit constexpr Array(u_index length) : length(length){
		if(length < 0 || length > 1024 * 1024 * 1024){
			throw yzrilyzr_lang::IndexOutOfBoundsException("Check size failed");
		}
		_array=new T[length]();
		this->refDoConstruct(_array);
	}
	explicit Array(const std::vector<T> &init) : length(init.size()){
		if(length < 0 || length > 1024 * 1024 * 1024){
			throw yzrilyzr_lang::IndexOutOfBoundsException("Check size failed");
		}
		_array=new T[length]();
		if(std::is_trivially_copyable<T>::value){
			std::copy(init.begin(), init.end(), _array);
		} else{
			for(size_t i=0, j=init.size();i < j;i++){
				_array[i]=init[i];
			}
		}
		this->refDoConstruct(_array);
	}

	~Array(){
		this->refDoDestruct();
	}
	static Array<T> wrap(T * arr, u_index length){
		return Array<T>(arr, length, true);
	}
	static Array<T> wrap_ref(T * arr, u_index length){
		return Array<T>(arr, length, false);
	}
	Array<T> clone() const{
		Array<T> a1(length);
		std::memcpy(a1._array, _array, length * sizeof(T));
		return a1;
	}

	T & operator[](u_index index) const{
		return _array[index];
	}
	T & at(u_index index) const{
		if(index < 0)throw yzrilyzr_lang::IndexOutOfBoundsException("index < 0");
		if(index >= length) throw yzrilyzr_lang::IndexOutOfBoundsException(
			yzrilyzr_lang::String("index (") + std::to_string(index) + ") >= length (" +
			std::to_string(length) + ")");
		return _array[index];
	}

	T * begin() const{
		return _array;
	}

	T * end() const{
		return _array + length;
	}

	yzrilyzr_lang::String toString() const override{
		yzrilyzr_lang::String ss="[";
		for(u_index i=0; i < length - 1; i++){
			ss << yzrilyzr_lang::ObjectUtil::toString(_array[i]) << ", ";
		}
		if(length > 0){
			ss << yzrilyzr_lang::ObjectUtil::toString(_array[length - 1]);
		}
		ss << "]";
		return ss;
	}
	void fill(const T & val){
		for(u_index i=0; i < length; i++){
			_array[i]=val;
		}
	}
	bool operator==(std::nullptr_t n)const{
		return _array == nullptr || length == -1;
	}
	bool operator!=(std::nullptr_t n) const{
		return !(*this == nullptr);
	}
	bool empty() const{
		return _array == nullptr || length == 0 || length == -1;
	}

	std::string getDebugString() const{
		return toString().tostring(yzrilyzr_lang::LOCAL);
	}
	};

	typedef Array<int8_t> ByteArray;
	typedef Array<int16_t> ShortArray;
	typedef Array<int32_t> IntArray;
	typedef Array<int64_t> LongArray;
	typedef Array<float> FloatArray;
	typedef Array<double> DoubleArray;
}